#pragma once

#include "OrionFlames.h"

namespace OrionFlames
{
//Glut needs to have a window created in order to use any of its functions, even if they don't depend on that window.
//We clone a few of the functions here to make them independent of Glut.
class OFAPI GlutClone
{
public:
	static void GlutSolidCube(GLdouble dSize)
	{
		double size = dSize * 0.5;

#define V(a,b,c) glVertex3d(a size, b size, c size);
#define N(a,b,c) glNormal3d(a, b, c);

		glBegin( GL_QUADS );
			N( 1.0, 0.0, 0.0); V(+, -, +); V(+, -, -); V(+, +, -); V(+, +, +);
			N( 0.0, 1.0, 0.0); V(+, +, +); V(+, +, -); V(-, +, -); V(-, +, +);
			N( 0.0, 0.0, 1.0); V(+, +, +); V(-, +, +); V(-, -, +); V(+, -, +);
			N(-1.0, 0.0, 0.0); V(-, -, +); V(-, +, +); V(-, +, -); V(-, -, -);
			N( 0.0,-1.0, 0.0); V(-, -, +); V(-, -, -); V(+, -, -); V(+, -, +);
			N( 0.0, 0.0,-1.0); V(-, -, -); V(-, +, -); V(+, +, -); V(+, -, -);
		glEnd();

#undef V
#undef N
	}

	static void GlutSolidSphere(GLdouble radius, GLint slices, GLint stacks)
	{
		int i,j;

		/* Adjust z and radius as stacks are drawn. */

		double z0,z1;
		double r0,r1;

		/* Pre-computed circle */

		double *sint1,*cost1;
		double *sint2,*cost2;

		CircleTable(&sint1,&cost1,-slices);
		CircleTable(&sint2,&cost2,stacks*2);

		/* The top stack is covered with a triangle fan */

		z0 = 1.0;
		z1 = cost2[(stacks>0)?1:0];
		r0 = 0.0;
		r1 = sint2[(stacks>0)?1:0];

		glBegin(GL_TRIANGLE_FAN);

			glNormal3d(0,0,1);
			glVertex3d(0,0,radius);

			for (j=slices; j>=0; j--)
			{
				glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
				glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
			}

		glEnd();

		//Cover each stack with a quad strip, except the top and bottom stacks.

		for( i=1; i<stacks-1; i++ )
		{
			z0 = z1; z1 = cost2[i+1];
			r0 = r1; r1 = sint2[i+1];

			glBegin(GL_QUAD_STRIP);

				for(j=0; j<=slices; j++)
				{
					glNormal3d(cost1[j]*r1,        sint1[j]*r1,        z1       );
					glVertex3d(cost1[j]*r1*radius, sint1[j]*r1*radius, z1*radius);
					glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
					glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
				}

			glEnd();
		}

		//The bottom stack is covered with a triangle fan.

		z0 = z1;
		r0 = r1;

		glBegin(GL_TRIANGLE_FAN);

			glNormal3d(0,0,-1);
			glVertex3d(0,0,-radius);

			for (j=0; j<=slices; j++)
			{
				glNormal3d(cost1[j]*r0,        sint1[j]*r0,        z0       );
				glVertex3d(cost1[j]*r0*radius, sint1[j]*r0*radius, z0*radius);
			}

		glEnd();

		//Release sin and cos tables

		free(sint1);
		free(cost1);
		free(sint2);
		free(cost2);
	}

private:
	//Compute lookup table of cos and sin values forming a cirle
	//
	//Notes:
	//   It is the responsibility of the caller to free these tables
	//   The size of the table is (n+1) to form a connected loop
	//   The last entry is exactly the same as the first
	//   The sign of n can be flipped to get the reverse loop
	static void CircleTable(double **sint,double **cost,const int n)
	{
		int i;

		/* Table size, the sign of n flips the circle direction */

		const int size = abs(n);

		/* Determine the angle between samples */

		const double angle = 2*M_PI/(double)( ( n == 0 ) ? 1 : n );

		/* Allocate memory for n samples, plus duplicate of first entry at the end */

		*sint = (double *) calloc(sizeof(double), size+1);
		*cost = (double *) calloc(sizeof(double), size+1);

		/* Bail out if memory allocation fails, fgError never returns */

		if (!(*sint) || !(*cost))
		{
			free(*sint);
			free(*cost);
			//fgError("Failed to allocate memory in fghCircleTable");
		}

		/* Compute cos and sin around the circle */

		(*sint)[0] = 0.0;
		(*cost)[0] = 1.0;

		for (i=1; i<size; i++)
		{
			(*sint)[i] = sin(angle*i);
			(*cost)[i] = cos(angle*i);
		}

		/* Last sample is duplicate of the first */

		(*sint)[size] = (*sint)[0];
		(*cost)[size] = (*cost)[0];
	}
};
}